﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Rappelz.GameServer.Database;

namespace Rappelz.GameServer
{

    public class ArPosition
    {
        public ArPosition(float x, float y, float z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
            this.face = 0;
        }

        public ArPosition(ArPosition _src)
        {
            this.x = _src.x;
            this.y = _src.y;
            this.z = _src.z;
            this.face = _src.face;
        }


        public ArPosition()
        {

        }

        public bool IsEqual(ArPosition cmp)
        {
            return !(this.x != cmp.x || this.y != cmp.y || this.z != cmp.z);
        }

//         public uint GetRX()
//         {
// 
//         }
// 
//         public uint GetRY()
//         {
// 
//         }

        public float GetDistance(ArPosition pos)
        {
            float xx,yy;

            xx = pos.x - this.x;
            yy = pos.y - this.y;
            xx = (float) Math.Sqrt(yy * yy + xx * xx);
            return xx;
        }

//         public bool operator==(ArPosition _that)
//         {
// 
//         }

        public float GetX()
        {
            return x;
        }

        public float GetY()
        {
            return y;
        }

        public float GetZ()
        {
            return z;
        }

        public void Copy(ArPosition _src)
        {
            this.x = _src.x;
            this.y = _src.y;
            this.z = _src.z;
            this.face = _src.face;
        }

        public void Reset()
        {
            this.x = 0;
            this.y = 0;
            this.z = 0;
            this.face = 0;
        }
        
        public float x;
        public float y;
        public float z;
        public float face;
    }

    public class ArMoveVector : ArPosition
    {
        public ArMoveVector()
        {

        }
        public ArMoveVector(ArMoveVector src)
        {
            this.x = src.x;
            this.y = src.y;
            this.z = src.z;
            this.face = src.face;
            this.bIsMoving = src.bIsMoving;
            this.bWithZMoving = src.bWithZMoving;
            this.speed = src.speed;
            this.start_time = src.start_time;
            this.proc_time = src.proc_time;
            foreach (MoveInfo mi in src.ends)
            {
                this.ends.Add(new MoveInfo(mi.end, mi.end_time));
            }
            this.bHasDirectionChanged = src.bHasDirectionChanged;
            this.direction.x = src.direction.x;
            this.direction.y = src.direction.y;
            this.direction.z = src.direction.z;
            this.direction.face = src.direction.face;

        }

        public class MoveInfo
        {
            public MoveInfo(ArPosition pos, uint t)
            {
                end.x = pos.x;
                end.y = pos.y;
                end.z = pos.z;
                end.face = pos.face;
                end_time = t;
            }
            public ArPosition end = new ArPosition();
            public uint end_time;
        }

//         Function       :   public void ArMoveVector::ArMoveVector(const struct ArMoveVector &)
//         Function       :   public void ArMoveVector::ArMoveVector()

        public bool Step(uint current_time)
        {
            bool res;
            List<MoveInfo> removed = new List<MoveInfo>();

            if (this.proc_time < current_time && this.bIsMoving )
            {
                foreach(MoveInfo info in this.ends)
                {
                    if (current_time < info.end_time)
                    {
                        uint et = current_time - this.proc_time;
                        float fet = (float)et / (float)(info.end_time - this.proc_time);
                        this.x = (info.end.x - this.x) * fet + this.x;
                        this.y = (info.end.y - this.y) * fet + this.y;
                        if (this.bWithZMoving)
                        {
                            this.z = (info.end.z - this.z) * fet + this.z;
                        }
                        break;
                    }
                    this.x = info.end.x;
                    this.y = info.end.y;
                    this.z = info.end.z;
                    this.proc_time = info.end_time;
                    removed.Add(info);
                    if (this.ends.Count > 0)
                    {
                        this.SetDirection(this.ends[0].end);
                    }
                }
                this.proc_time = current_time;
                foreach(MoveInfo info in removed)
                {
                    this.ends.Remove(info);
                }

                if (this.ends.Count < 1 || this.x == this.ends.Last().end.x && this.y == this.ends.Last().end.y && this.z == this.ends.Last().end.z)
                {
                    this.bIsMoving = false;
                    this.ends.Clear();
                    res = true;
                }
                else
                {
                    res = false;
                }
            }
            else
            {
                res = false;
            }
            return res;

        }

        public void SetMultipleMove(List<ArPosition> _to, byte _speed, uint _start_time, uint current_time)
        {
            this.ends.Clear();

            if (_to.Count > 0)
            {
                this.speed = _speed;
                uint ct = _start_time;
                if (ct == 0)
                    ct = Globals.GetArTime();
                this.start_time = ct;
                this.proc_time = ct;
                uint start_time = ct;
                this.SetDirection(_to[0]);
                float before_x = this.x;
                float before_y = this.y;
                foreach (ArPosition pos in _to)
                {
                    float cx = pos.x - before_x;
                    float cy = pos.y - before_y;
                    before_x = pos.x;
                    before_y = pos.y;
                    float length = (float)Math.Sqrt(cy * cy + cx * cx);
                    uint end_time = (uint)(length / ((float)this.speed / 30.0f) + (float)start_time);
                    MoveInfo mi = new MoveInfo(pos, end_time);
                    ends.Add(mi);
                    start_time = end_time;
                }

                this.bIsMoving = true;
            }

        }

        public void SetMove(ArPosition _to, byte _speed, uint _start_time, uint current_time)
        {
            double v9;
            double v10;
            double lengtha;
            double lengthb;


            this.ends.Clear();
            this.speed = _speed;
            uint st = _start_time;
            if (_start_time == 0)
                st = Globals.GetArTime();
            this.start_time = st;
            this.proc_time = st;
            float X = _to.x - this.x;
            float Y = _to.y - this.y;
            this.SetDirection(_to);
            lengtha = Math.Sqrt(Y * Y + X * X);
            lengthb = this.speed;
            v9 = lengtha / (lengthb / 30.0);
            v10 = this.start_time;
            if ((this.start_time & 0x80000000) != 0 )
                v10 = v10 + 4294967300.0;

            MoveInfo mi = new MoveInfo(_to, this.start_time);
            mi.end_time = (uint)(v9 + v10);

            this.ends.Add(mi);
            this.bIsMoving = true;
        }

//         Function       :   public void SetKnockBack(const struct ArPosition &, unsigned char, unsigned long, unsigned long)
//         Function       :   public float GetTX()
//         Function       :   public float GetTY()
//         Function       :   public float GetTZ()
//         Function       :   public float GetX()
//         Function       :   public float GetY()
//         Function       :   public float GetZ()
//         Function       :   public float GetFace()
//         Function       :   public void SetFace(float)
//         Function       :   public short GetDegree()

        public void SetDirection(ArPosition pos)
        {
            float Y;
            float X;

            X = pos.x - this.x;
            Y = pos.y - this.y;
            if (0.0 != X || 0.0 != Y)
            {
                this.bHasDirectionChanged = true;
                this.direction.x = pos.x;
                this.direction.y = pos.y;
                this.direction.z = pos.z;
                this.face = (float)Math.Atan2(Y, X);
            }
        }
//         Function       :   public void SetCurrentXY(float, float)
//         Function       :   public void SetCurrentZ(float)
//         Function       :   public void SetCurrentXYZ(float, float, float)
//         Function       :   public const struct ArPosition & GetPos()

        public ArPosition GetTargetPos()
        {
            ArPosition result;

            if (this.ends.Count > 0)
                result = this.ends.Last().end;
            else
                result = this;
            return result;

        }
//         Function       :   public unsigned long GetTargetPosTime()
//         Function       :   public unsigned long GetProcTime()
//         Function       :   public const class std::vector<ArMoveVector::MOVE_INFO,std::allocator<ArMoveVector::MOVE_INFO> > & GetTargetPosList()

        public bool IsMoving(uint t)
        {
            if (this.bIsMoving && (this.ends.Count != 0))
                return t < this.ends.Last().end_time;
            else
                return false;
        }
//         Function       :   public bool IsMoving()
//         Function       :   public void SetMoving(bool)
//         Function       :   public bool HasDirectionChanged()
//         Function       :   public const struct ArPosition & GetDirection()
//         Function       :   public void SetCurrentPos(const struct ArPosition &)
//         Function       :   public unsigned long GetStartTime()
//         Function       :   public unsigned char GetSpeed()
//         Function       :   public unsigned int GetWayPointCount()
        public bool bIsMoving;                                          // 0x10
        public bool bWithZMoving;                                       // 0x11
        public byte speed;                                              // 0x12
        public uint start_time;                                         // 0x14
        public uint proc_time;                                          // 0x18
        public List<MoveInfo> ends = new List<MoveInfo>();              // 0x1C// 
        public bool bHasDirectionChanged;                               // 0x2C
        public ArPosition direction = new ArPosition();                 // 0x30
    }


    public class BaseObject : SchedulerObject
    {
//         public enum ObjectPriority : int
//         {
//             Null = -1,
//             Idle = 0,
//             Lowest = 1,
//             Low = 2,
//             Normal = 3,
//             High = 4,
//             Highest = 5,
//             Dummy = 6,
//         }
// UserDefinedType: ArObject
// BaseClass      :   ArSchedulerObject, offset = 0x0
// VTable         :     , Type:  *
// Function       :     public void ArSchedulerObject(const struct ArSchedulerObject &)
// Function       :     public void ArSchedulerObject::ArSchedulerObject()
        public uint GetHandle()
        {
            return m_hHandle;
        }

        public void DBQuery(IDBProc proc)
        {
            lock (m_lQueryList)
            {
                if (m_lQueryList.Count == 0)
                {
                    DatabaseProcess p = GameDBManager.dbProcess;
                    proc.SetCB(this);
                    GameDBManager.Instance.Push(p, proc);
                }
                m_lQueryList.Enqueue(proc);
            }
        }

        public void onEndQuery()
        {
            lock (m_lQueryList)
            {
                m_lQueryList.Dequeue();
                if (m_lQueryList.Count > 0)
                {
                    DatabaseProcess p = GameDBManager.dbProcess;
                    IDBProc proc = m_lQueryList.Peek();
                    proc.SetCB(this);
                    GameDBManager.Instance.Push(p, proc);
                }
            }
        }

//         public ObjectPriority pending_priority = ObjectPriority.Null;                         // 0x1C
//         public ObjectPriority priority = ObjectPriority.Idle;                                 // 0x28

        public enum ObjectType : int
        {
            Static = 0,
            Movable = 1,
            Client = 2,
        }

        public ObjectType GetObjectType()
        {
            return (ObjectType)m_nArObjectType;
        }

        public int region_index = -1;                    // 0x40
// Function       :   public void ArObject(const struct ArObject &)
// Function       :   public void ArObject::ArObject(enum ArObject::ObjectType)
// Function       :   public float GetX()
// Function       :   public float GetY()
// Function       :   public float GetZ()
// Function       :   public unsigned char GetLayer()

        public void AddNoise(int r1, int r2, int v)
        {
            float prev_x = mv.x;
            float prev_y = mv.y;

            double rs = (double)Globals.nRegionSize;
            int tx = (int) (mv.x / rs);
            int ty = (int) (mv.y / rs);
            mv.x = (float)(r1 % v - v / 2) + mv.x;
            mv.y = (float)(r2 % v - v / 2) + mv.y;
            if ((int)(mv.x / rs) != tx)
                mv.x = prev_x;
            if ((int)(mv.y / rs) != ty)
                mv.y = prev_y;
        }

// Function       :   public const struct ArPosition & GetPos()
// Function       :   public const struct ArMoveVector & GetMv()
// Function       :   public unsigned int GetRX()
// Function       :   public unsigned int GetRY()

        public ArPosition GetCurrentPosition(uint t)
        {
            ArPosition result = new ArPosition();
            ArMoveVector _mv;

            if (this.mv.bIsMoving && this.bIsInWorld )
            {
                _mv = new ArMoveVector(this.mv);
                _mv.Step(t);
                result.x = _mv.x;
                result.y = _mv.y;
                result.z = _mv.z;
                result.face = _mv.face;
            }
            else
            {
                result.x = this.mv.x;
                result.y = this.mv.y;
                result.z = this.mv.z;
                result.face = this.mv.face;
            }
            return result;
        }
// Function       :   public void SetTag(void *)
// Function       :   public void * GetTag()
// Function       :   public bool IsMoving(unsigned long)
// Function       :   public bool IsMoving()
// Function       :   public bool IsInWorld()

        public bool Step(uint tm)
        {
            bool res = this.mv.Step(tm);
            if (res)
            {
                ArPosition pos = this.mv.GetTargetPos();
                this.mv.x = pos.x;
                this.mv.y = pos.y;
                this.mv.z = pos.z;
                this.mv.face = pos.face;
            }
            this.lastStepTime = tm;
            return res;

        }

        public void SetMove(float x, float y, byte _speed, uint _start_time)
        {
            ArPosition p = new ArPosition(x, y, 0);
            this.mv.SetMove(p, _speed, _start_time, Globals.GetArTime());
            this.lastStepTime = this.mv.start_time;
        }

        public void SetMove(ArPosition _to, byte _speed, uint _start_time)
        {
            this.mv.SetMove(_to, _speed, _start_time, Globals.GetArTime());
            this.lastStepTime = this.mv.start_time;
        }


        public void SetMultipleMove(List<ArPosition> _to, byte _speed, uint _start_time)
        {
            mv.SetMultipleMove(_to,_speed, _start_time, Globals.GetArTime());
            this.lastStepTime = this.mv.start_time;
        }

        public void StopMove()
        {
            this.mv.ends.Clear();
            this.mv.bIsMoving = false;
        }
// Function       :   public float GetFace()
// Function       :   public void SetFace(float)
// Function       :   public float GetTX()
// Function       :   public float GetTY()
// Function       :   public float GetTZ()
// Function       :   public const class std::vector<ArMoveVector::MOVE_INFO,std::allocator<ArMoveVector::MOVE_INFO> > & GetTargetPosList()
// Function       :   public const struct ArPosition & GetTargetPos()
// Function       :   public unsigned char GetSpeed()
// Function       :   public unsigned long GetStartTime()
// Function       :   public void SetDirection(const struct ArPosition &)
// Function       :   public bool IsLookingRegion(unsigned int, unsigned int)
        public void SetCurrentXY(float x, float y)
        {
            if (this.prev_rx > -1 )
            {
                this.prev_rx = (int)(this.mv.x / (double)Globals.nRegionSize);
                this.prev_ry = (int)(this.mv.y / (double)Globals.nRegionSize);
            }
            this.mv.x = x;
            this.mv.y = y;
        }

        public bool IsSameObject(BaseObject cmp)
        {
            if (cmp == null)
                return false;
            return this.m_hHandle == cmp.m_hHandle;
        }
// Function       :   public void SetCurrentZ(float)
// Function       :   public void SetCurrentLayer(unsigned char)

        public bool bIsInWorld;                             // 0x44
        public bool bIsNearClient;                          // 0x45, Member, Type: volatile bool, 
        public int prev_rx = -1;                                 // 0x48
        public int prev_ry = -1;                                 // 0x4C
        public bool bIsRegionChanging;                      // 0x50
        public uint lastStepTime;                           // 0x54
        public ArRegion pRegion;                            // 0x58
        public uint nListIndex;                             // 0x5C
        public uint nActiveIndex;                           // 0x60
// Function       :   protected void ArObject::~ArObject()
        public byte m_nArObjectType;                        // 0x64
// Data           :   this+0x68, Member, Type: void *, pTag
        public ArMoveVector mv = new ArMoveVector();        // 0x6C
        public byte m_layer;                                // 0xAC
        public bool bIsDeleted;                             // 0xAD
        public Queue<IDBProc> m_lQueryList = new Queue<IDBProc>();
        public uint m_hHandle;

        public uint region_x;
        public uint region_y;
          
    }
}
